package controller

import (
	//	"fmt"
	"encoding/json"
	"github.com/bmizerany/pat"
	jwt "github.com/dgrijalva/jwt-go"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
)

// location of the files used for signing and verification
const (
	privKeyPath = "keys/app.rsa"     // openssl genrsa -out app.rsa keysize
	pubKeyPath  = "keys/app.rsa.pub" // openssl rsa -in app.rsa -pubout > app.rsa.pub
)

//token 状态
const (
	TokenPass = 0
	TokenExpired
	TokenNo
)

// keys are held in global variables
// i havn't seen a memory corruption/info leakage in go yet
// but maybe it's a better idea, just to store the public key in ram?
// and load the signKey on every signing request? depends on  your usage i guess
var (
	VerifyKey, SignKey []byte
	LoginUserInfo      map[string]interface{}
	TokenStatus        int
)

type AjaxReturn struct {
	Code int
	Msg  string
	Data interface{}
}

type NextPreer interface {
	NextPre()
}

type ControllerInterface interface {
	Init(ResponseWriter http.ResponseWriter, Request *http.Request, ActionName string, ControllerName string, AppController interface{}, Router *pat.PatternServeMux)
	Pre()
	End()
}

type Base struct {
	ResponseWriter http.ResponseWriter
	Request        *http.Request
	ActionName     string
	ControllerName string
	AppController  interface{}
	Router         *pat.PatternServeMux
}

func (this *Base) Init(ResponseWriter http.ResponseWriter, Request *http.Request, ActionName string, ControllerName string, AppController interface{}, Router *pat.PatternServeMux) {

	var err error

	SignKey, err = ioutil.ReadFile(privKeyPath)
	if err != nil {
		log.Fatal("Error reading private key")
		return
	}

	VerifyKey, err = ioutil.ReadFile(pubKeyPath)
	if err != nil {
		log.Fatal("Error reading private key")
		return
	}

	this.ResponseWriter = ResponseWriter
	this.Request = Request
	this.ActionName = ActionName
	this.ControllerName = ControllerName
	this.AppController = AppController
	this.Router = Router
	this.TokenCheck()
	this.Pre()
}
func (this *Base) Pre() {
	if app, ok := this.AppController.(NextPreer); ok {
		app.NextPre()
	}
}
func (this *Base) End() {
	//	fmt.Fprint(this.ResponseWriter, "End")
}

func (this *Base) GetIntUrlParam(v ...interface{}) (int, bool) {
	par_num := len(v)
	if par_num < 1 {

		return 0, false

	} else if par_num == 1 {

		if val_key, ok := v[0].(string); ok {
			val := this.Request.URL.Query().Get(val_key)
			if val_end, err := strconv.Atoi(val); err == nil {
				return val_end, true
			}
		}
		return 0, false

	} else {

		var par_default int

		if val_default, ok := v[1].(int); ok {
			par_default = val_default
		} else {
			par_default = 0
		}

		if val_key, ok := v[0].(string); ok {
			val := this.Request.URL.Query().Get(val_key)
			if val_end, err := strconv.Atoi(val); err == nil {
				return val_end, true
			}
		}
		return par_default, true
	}
}

func (this *Base) GetStringUrlParam(v ...interface{}) string {

	par_num := len(v)
	if par_num < 1 {
		return ""

	} else if par_num == 1 {

		if val_key, ok := v[0].(string); ok {

			return this.Request.URL.Query().Get(val_key)

		} else {
			//参数不存在
			return ""
		}

	} else {

		var par_default string

		if val_default, ok := v[1].(string); ok {
			par_default = val_default
		} else {
			par_default = ""
		}

		if val_key, ok := v[0].(string); ok {

			return this.Request.URL.Query().Get(val_key)

		} else {
			//参数不存在
			return par_default
		}
	}
}

func (this *Base) GetIntPostParam(v ...interface{}) (int, bool) {

	par_num := len(v)
	if par_num < 1 {

		return 0, false

	} else if par_num == 1 {

		if val_key, ok := v[0].(string); ok {

			this.Request.ParseForm()
			if len(this.Request.PostForm[val_key]) > 0 {
				if val_end, err := strconv.Atoi(this.Request.PostForm[val_key][0]); err == nil {
					return val_end, true
				}
			}
		}
		return 0, false

	} else {

		var par_default int

		if val_default, ok := v[1].(int); ok {
			par_default = val_default
		} else {
			par_default = 0
		}

		if val_key, ok := v[0].(string); ok {

			this.Request.ParseForm()
			if len(this.Request.PostForm[val_key]) > 0 {
				if val_end, err := strconv.Atoi(this.Request.PostForm[val_key][0]); err == nil {
					return val_end, true
				}
			}

		}
		return par_default, true
	}
}

func (this *Base) GetStringPostParam(v ...interface{}) string {

	par_num := len(v)
	if par_num < 1 {

		return ""

	} else if par_num == 1 {

		if val_key, ok := v[0].(string); ok {

			this.Request.ParseForm()
			if len(this.Request.PostForm[val_key]) > 0 {
				return this.Request.PostForm[val_key][0]
			}
		}
		return ""

	} else {

		var par_default string

		if val_default, ok := v[1].(string); ok {
			par_default = val_default
		} else {
			par_default = ""
		}

		if val_key, ok := v[0].(string); ok {

			this.Request.ParseForm()
			if len(this.Request.PostForm[val_key]) > 0 {
				return this.Request.PostForm[val_key][0]
			}

		}
		return par_default
	}
}

func (this *Base) AjaxReturnFun(code int, msg string, data interface{}) {
	m := AjaxReturn{code, msg, data}
	b, err := json.Marshal(m)
	if err == nil {
		this.ResponseWriter.Write(b)
	} else {
		this.ResponseWriter.Write([]byte("{\"code\":0,\"msg\":\"系统异常\",\"data\":\"\"}"))
	}
}

/**
 *TOKEN 验证
 */
func (this *Base) TokenCheck() {

	user_token := this.GetStringUrlParam("token")

	if user_token == "" {
		TokenStatus = TokenNo
		return
	}

	// validate the token
	token, err := jwt.Parse(user_token, func(token *jwt.Token) (interface{}, error) {
		// since we only use the one private key to sign the tokens,
		// we also only use its public counter part to verify
		return VerifyKey, nil
	})

	switch err.(type) {

	case nil: // no error

		if !token.Valid { // but may still be invalid
			TokenStatus = TokenNo
		}

		LoginUserInfo, _ = token.Claims["userinfo"].(map[string]interface{})
		TokenStatus = TokenPass
		return

	case *jwt.ValidationError: // something was wrong during the validation
		vErr := err.(*jwt.ValidationError)

		switch vErr.Errors {
		case jwt.ValidationErrorExpired:

			TokenStatus = TokenExpired
			return

		default:
			log.Print("You token be " + vErr.Error())
			TokenStatus = TokenNo
			return
		}

	default: // something else went wrong
		log.Print("You token be " + err.Error())
		TokenStatus = TokenNo
		return
	}

}

func SqlDemo(w http.ResponseWriter, r *http.Request) {

	//	data := models.Query("SELECT * FROM ittr_cards")
	//	fmt.Fprintf(w, "\n%#v\n", data)
}
